package org.gdstash.character;

import org.gdstash.file.GDReader;
import org.gdstash.file.GDWriter;
import org.gdstash.util.FileVersionException;
import org.gdstash.util.GDMsgFormatter;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

public class GDCharStats {
    private static final int VERSION_7 = 7;
    private static final int VERSION_9 = 9;
    private static final int VERSION_11 = 11;
    private static final int BLOCK = 16;
    private int version;
    private int playTime;
    private int deaths;
    private int kills;
    private int experienceFromKills;
    private int healthPotionsUsed;
    private int manaPotionsUsed;
    private int maxLevel;
    private int hitsReceived;
    private int hitsInflicted;
    private int criticalHitsInflicted;
    private int criticalHitsReceived;
    private float greatestDamageInflicted;
    private StatsPerDifficulty[] statsDifficulty;
    private int championKills;
    private float lastHit;
    private float lastHitBy;
    private float greatestDamageReceived;
    private int heroKills;
    private int itemsCrafted;
    private int relicsCrafted;
    private int transcendentRelicsCrafted;
    private int mythicalRelicsCrafted;
    private int shrinesRestored;
    private int oneShotChestsOpened;
    private int loreNotesCollected;
    private int survivalGreatestWave;
    private int survivalGreatestScore;
    private int survivalDefensesBuilt;
    private int survivalPowerUpsActivated;
    private List<GDCharSkillMap> skillMap;
    private int endlessSouls;
    private int endlessEssence;
    private byte difficultySkip;
    private int unknown1;
    private int unknown2;
    private boolean changed;

    public GDCharStats() {
        this.playTime = 0;
        this.deaths = 0;
        this.kills = 0;
        this.experienceFromKills = 0;
        this.healthPotionsUsed = 0;
        this.manaPotionsUsed = 0;
        this.maxLevel = 0;
        this.hitsReceived = 0;
        this.hitsInflicted = 0;
        this.criticalHitsInflicted = 0;
        this.criticalHitsReceived = 0;
        this.greatestDamageInflicted = 0.0f;
        this.statsDifficulty = new StatsPerDifficulty[3];
        for (int i = 0; i < this.statsDifficulty.length; ++i) {
            this.statsDifficulty[i] = new StatsPerDifficulty();
        }
        this.championKills = 0;
        this.lastHit = 0.0f;
        this.lastHitBy = 0.0f;
        this.greatestDamageReceived = 0.0f;
        this.heroKills = 0;
        this.itemsCrafted = 0;
        this.relicsCrafted = 0;
        this.transcendentRelicsCrafted = 0;
        this.mythicalRelicsCrafted = 0;
        this.shrinesRestored = 0;
        this.oneShotChestsOpened = 0;
        this.loreNotesCollected = 0;
        this.survivalGreatestWave = 0;
        this.survivalGreatestScore = 0;
        this.survivalDefensesBuilt = 0;
        this.survivalPowerUpsActivated = 0;
        this.skillMap = new LinkedList<GDCharSkillMap>();
        this.endlessSouls = 0;
        this.endlessEssence = 0;
        this.difficultySkip = 0;
        this.unknown1 = 0;
        this.unknown2 = 0;
    }

    public boolean hasChanged() {
        return this.changed;
    }

    public int getDeaths() {
        return this.deaths;
    }

    public int getPlayTime() {
        return this.playTime;
    }

    public String getPlayTimeAsDHM() {
        String s = null;
        int hours = this.playTime / 3600;
        final int days = hours / 24;
        int secs = this.playTime % 3600;
        final int mins = secs / 60;
        hours %= 24;
        secs %= 60;
        s = String.format("%02d", days);
        s = s + ":" + String.format("%02d", hours);
        s = s + ":" + String.format("%02d", mins);
        return s;
    }

    public int getGreatestDamageInflicted() {
        return (int) this.greatestDamageInflicted;
    }

    public void setGreatestDamageInflicted(final float damage) {
        this.greatestDamageInflicted = damage;
    }

    public int getKillsMonster() {
        return this.kills;
    }

    public int getKillsHero() {
        return this.championKills;
    }

    public int getKillsNemesis() {
        int nemesisKills = 0;
        for (int i = 1; i < this.statsDifficulty.length; ++i) {
            nemesisKills += this.statsDifficulty[i].nemesisKills;
        }
        return nemesisKills;
    }

    public int getLoreNotesCollected() {
        return this.loreNotesCollected;
    }

    public int getOneShotChestsOpened() {
        return this.oneShotChestsOpened;
    }

    public int getItemsCrafted() {
        return this.itemsCrafted;
    }

    public int getRelicsCrafted() {
        return this.relicsCrafted;
    }

    public int getRelicsTranscendentCrafted() {
        return this.transcendentRelicsCrafted;
    }

    public int getRelicsMythicalCrafted() {
        return this.mythicalRelicsCrafted;
    }

    public int getGreatestDamageReceived() {
        return (int) this.greatestDamageReceived;
    }

    public void setShrinesRestored(final int shrinesRestored) {
        this.shrinesRestored = shrinesRestored;
        this.changed = true;
    }

    public void setMaxLevel(final int level) {
        this.maxLevel = level;
        this.changed = true;
    }

    public void setGreatestDamageReceived(final float damage) {
        this.greatestDamageReceived = damage;
    }

    public void setDeaths(final int deaths) {
        this.deaths = deaths;
        this.changed = true;
    }

    public void read() throws IOException {
        int val = 0;
        final GDReader.Block block = new GDReader.Block();
        val = GDReader.readBlockStart(block);
        if (val != 16) {
            throw new FileVersionException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
        }
        this.version = GDReader.readEncInt(true);
        if (this.version != 7 && this.version != 9 && this.version != 11) {
            throw new FileVersionException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
        }
        this.playTime = GDReader.readEncInt(true);
        this.deaths = GDReader.readEncInt(true);
        this.kills = GDReader.readEncInt(true);
        this.experienceFromKills = GDReader.readEncInt(true);
        this.healthPotionsUsed = GDReader.readEncInt(true);
        this.manaPotionsUsed = GDReader.readEncInt(true);
        this.maxLevel = GDReader.readEncInt(true);
        this.hitsReceived = GDReader.readEncInt(true);
        this.hitsInflicted = GDReader.readEncInt(true);
        this.criticalHitsInflicted = GDReader.readEncInt(true);
        this.criticalHitsReceived = GDReader.readEncInt(true);
        this.greatestDamageInflicted = GDReader.readEncFloat(true);
        for (int i = 0; i < this.statsDifficulty.length; ++i) {
            this.statsDifficulty[i].greatestMonsterKilledName = GDReader.readEncString();
            this.statsDifficulty[i].greatestMonsterKilledLevel = GDReader.readEncInt(true);
            this.statsDifficulty[i].greatestMonsterKilledLifeAndMana = GDReader.readEncInt(true);
            this.statsDifficulty[i].lastMonsterHit = GDReader.readEncString();
            this.statsDifficulty[i].lastMonsterHitBy = GDReader.readEncString();
        }
        this.championKills = GDReader.readEncInt(true);
        this.lastHit = GDReader.readEncFloat(true);
        this.lastHitBy = GDReader.readEncFloat(true);
        this.greatestDamageReceived = GDReader.readEncFloat(true);
        this.heroKills = GDReader.readEncInt(true);
        this.itemsCrafted = GDReader.readEncInt(true);
        this.relicsCrafted = GDReader.readEncInt(true);
        this.transcendentRelicsCrafted = GDReader.readEncInt(true);
        this.mythicalRelicsCrafted = GDReader.readEncInt(true);
        this.shrinesRestored = GDReader.readEncInt(true);
        this.oneShotChestsOpened = GDReader.readEncInt(true);
        this.loreNotesCollected = GDReader.readEncInt(true);
        for (int i = 0; i < this.statsDifficulty.length; ++i) {
            this.statsDifficulty[i].nemesisKills = GDReader.readEncInt(true);
        }
        if (this.version >= 9) {
            this.survivalGreatestWave = GDReader.readEncInt(true);
            this.survivalGreatestScore = GDReader.readEncInt(true);
            this.survivalDefensesBuilt = GDReader.readEncInt(true);
            this.survivalPowerUpsActivated = GDReader.readEncInt(true);
        }
        if (this.version >= 11) {
            this.skillMap.clear();
            for (int len = GDReader.readEncInt(true), j = 0; j < len; ++j) {
                final GDCharSkillMap map = new GDCharSkillMap();
                map.read();
                this.skillMap.add(map);
            }
            this.endlessSouls = GDReader.readEncInt(true);
            this.endlessEssence = GDReader.readEncInt(true);
            this.difficultySkip = GDReader.readEncByte();
        }
        this.unknown1 = GDReader.readEncInt(true);
        this.unknown2 = GDReader.readEncInt(true);
        GDReader.readBlockEnd(block);
        this.changed = false;
    }

    public void write() throws IOException {
        final GDReader.Block block = new GDReader.Block();
        GDWriter.writeBlockStart(block, 16);
        GDWriter.writeInt(this.version);
        GDWriter.writeInt(this.playTime);
        GDWriter.writeInt(this.deaths);
        GDWriter.writeInt(this.kills);
        GDWriter.writeInt(this.experienceFromKills);
        GDWriter.writeInt(this.healthPotionsUsed);
        GDWriter.writeInt(this.manaPotionsUsed);
        GDWriter.writeInt(this.maxLevel);
        GDWriter.writeInt(this.hitsReceived);
        GDWriter.writeInt(this.hitsInflicted);
        GDWriter.writeInt(this.criticalHitsInflicted);
        GDWriter.writeInt(this.criticalHitsReceived);
        GDWriter.writeFloat(this.greatestDamageInflicted);
        for (int i = 0; i < this.statsDifficulty.length; ++i) {
            GDWriter.writeString(this.statsDifficulty[i].greatestMonsterKilledName);
            GDWriter.writeInt(this.statsDifficulty[i].greatestMonsterKilledLevel);
            GDWriter.writeInt(this.statsDifficulty[i].greatestMonsterKilledLifeAndMana);
            GDWriter.writeString(this.statsDifficulty[i].lastMonsterHit);
            GDWriter.writeString(this.statsDifficulty[i].lastMonsterHitBy);
        }
        GDWriter.writeInt(this.championKills);
        GDWriter.writeFloat(this.lastHit);
        GDWriter.writeFloat(this.lastHitBy);
        GDWriter.writeFloat(this.greatestDamageReceived);
        GDWriter.writeInt(this.heroKills);
        GDWriter.writeInt(this.itemsCrafted);
        GDWriter.writeInt(this.relicsCrafted);
        GDWriter.writeInt(this.transcendentRelicsCrafted);
        GDWriter.writeInt(this.mythicalRelicsCrafted);
        GDWriter.writeInt(this.shrinesRestored);
        GDWriter.writeInt(this.oneShotChestsOpened);
        GDWriter.writeInt(this.loreNotesCollected);
        for (int i = 0; i < this.statsDifficulty.length; ++i) {
            GDWriter.writeInt(this.statsDifficulty[i].nemesisKills);
        }
        if (this.version >= 9) {
            GDWriter.writeInt(this.survivalGreatestWave);
            GDWriter.writeInt(this.survivalGreatestScore);
            GDWriter.writeInt(this.survivalDefensesBuilt);
            GDWriter.writeInt(this.survivalPowerUpsActivated);
        }
        if (this.version >= 11) {
            final int len = this.skillMap.size();
            GDWriter.writeInt(len);
            for (final GDCharSkillMap map : this.skillMap) {
                if (map != null) {
                    map.write();
                }
            }
            GDWriter.writeInt(this.endlessSouls);
            GDWriter.writeInt(this.endlessEssence);
            GDWriter.writeByte(this.difficultySkip);
        }
        GDWriter.writeInt(this.unknown1);
        GDWriter.writeInt(this.unknown2);
        GDWriter.writeBlockEnd(block);
        this.changed = false;
    }

    public int getByteSize() {
        int size = 0;
        final int val = 0;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        for (int i = 0; i < this.statsDifficulty.length; ++i) {
            size += 4;
            if (this.statsDifficulty[i].greatestMonsterKilledName != null) {
                size += this.statsDifficulty[i].greatestMonsterKilledName.length();
            }
            size += 4;
            size += 4;
            size += 4;
            if (this.statsDifficulty[i].lastMonsterHit != null) {
                size += this.statsDifficulty[i].lastMonsterHit.length();
            }
            size += 4;
            if (this.statsDifficulty[i].lastMonsterHitBy != null) {
                size += this.statsDifficulty[i].lastMonsterHitBy.length();
            }
        }
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4 * this.statsDifficulty.length;
        if (this.version >= 9) {
            size += 4;
            size += 4;
            size += 4;
            size += 4;
        }
        if (this.version >= 11) {
            size += 4;
            for (final GDCharSkillMap map : this.skillMap) {
                if (map != null) {
                    size += map.getByteSize();
                }
            }
            size += 4;
            size += 4;
            ++size;
        }
        size += 4;
        size += 4;
        size += 4;
        return size;
    }

    private static class StatsPerDifficulty {
        private String greatestMonsterKilledName;
        private int greatestMonsterKilledLevel;
        private int greatestMonsterKilledLifeAndMana;
        private String lastMonsterHit;
        private String lastMonsterHitBy;
        private int nemesisKills;
    }
}
